今天要提到的是TypeScript才有的語法,而TypeScript的這些語法擴展了JavaScript缺乏的一些OOP(Objected-oriented Programming)性質
在JavaScript裡,目前只有 public
和 private
用法,並且預設class的屬性和方法都是 public
,不過JavaScript執行環境必須支持ES10(ES2019)語法才能使用 private
(#
) 語法。
TypeScript在類似語法方面則是新增三個關鍵字 public
、protected
、private
:
public
代表類別定義的屬性或方法能在該類別以外的環境存取;private
表示類別定義的屬性或方法只能在被定義的類別內部存取,通常都會透過一個 public
函式來存取 private
屬性;protected
只能在被定義的類別和繼承(inherit)該類別的子類別(sub-class)所存取。class Person {
private id: number;
protected first_name: string;
protected last_name: string;
protected birth_year: number;
constructor(id: number, first_name: string, last_name: string, birth_year: number) {
this.id = id;
this.first_name = first_name;
this.last_name = last_name;
this.birth_year = birth_year;
}
public getName(): string {
return `${this.first_name} ${this.last_name}`;
});
public getBirthYear(): number {
return this.birth_year;
}
public introduce(): string {
return `My name is ${first_name}.`;
}
}
let Bob = new Person(1234567, 'Bob', 'Jonason', 1989);
console.log(Bob.getName()); // Bob Jonason
console.log(Bob.birth_year); // Compile error
console.log(Bob.getBirthYear()); // 1989
如果在class外部存取 private
屬性或方法,TypeScript能在編譯期就發現錯誤,例如上面嘗試在Person
外部存取Bob的 birth_year
屬性就出現編譯錯誤。
此外,TypeScript也有另一種比較簡潔的寫法 ─ 就是把宣告屬性和存取限制寫在constructor裡:
class Person {
constructor(private id: number, protected first_name: string, protected last_name: string, protected birth_year: number) {
this.id = id;
this.first_name = first_name;
this.last_name = last_name;
this.birth_year = birth_year;
}
public getName(): string {
return `${this.first_name} ${this.last_name}`;
});
public getBirthYear(): number {
return this.birth_year
}
public introduce(): string {
return `My name is ${first_name}.`
}
}
和JavaScript一樣,TypeScript類別的預設屬性或方法都是 public
,所以範例中的 public
關鍵字也可以省略。
readonly
是用來限制屬性(property)不能在constructor以外的地方被賦值,也不能被重新賦值。readonly
的優點是可以在class外部去作存取,但無法修改它。
假設這次在class外部新增一個計算年齡 calcAge
的函式,而 calcAge
不一定是只能給 Person
class使用,也能給有繼承 Person
birth_year
屬性的子類別和其他有 birth_year
屬性的class用,所以並沒有把 calcAge
放在 Person
裡。
因此這個範例希望可以存取 Person
物件的 birth_year
屬性:
class Person {
private id: number;
protected first_name: string;
protected last_name: string;
readonly birth_year: number; // 注意這行其實是public屬性
protected hasChildren: boolean;
constructor(id: number, first_name: string, last_name: string, age: number) {
this.id = id;
this.first_name = first_name;
this.last_name = last_name;
this.age = age;
}
resetBirthYear(): { // This is an error method
this.birth_year = 1900; // error
}
getName(): string {
return ${this.first_name} ${this.last_name}`;
};
getBirthYear(): number {
return this.birth_year;
}
}
const Bob = new Perso(1234567, 'Bob', 'Jonason', 35, true);
function calcAge(curr, birth_year){
return curr-birth_year;
}
console.log(calcAge(2022, Bob.birth_year)); // 33
這時候就能善加利用 readonly
關鍵字讓 birth_year
能被外部函式存取,又能避免修改到屬性。
雖然 readonly
用法聽起來跟 const
的作用有點像,但是 const
是用來修飾變數(variable)的關鍵字。
最後要介紹一種蠻特別的類別類型 ─ abstract class。
abstract class本身無法用 new
關鍵字去建立一個屬於該類別的物件,abstract class會定義至少一個抽象方法(abstract method),讓其他類別去繼承並且實作(implement)。
抽象方法是指abstract class會寫出一些子類別必須擁有的方法,但這些方法只有方法名稱,並不會具體地在方法內部寫出該方法會做些什麼,而是讓其他類別繼承方法所屬的抽象類別之後,再詳細地實踐方法的行為。
光聽概念可能會對抽象類別的定義有點模糊,接下來就把前面的 Person class 改寫成abstract class,而要創造abstract class需要用 abstract
關鍵字:
abstract class Person {
constructor(private first_name: string, private last_name: string){}
abstract getBirthYear();
abstract getAbility();
get introduce():{
return `${this.first_name} ${this.last_name} can {this.getAbility()}.`
}
}
class SuperHero extends Person {
constructor(first_name: string, last_name: string, private birth_year: number, private ability: string){
super(first_name, last_name);
this.birth_year = age;
this.ability = ability;
}
getBirthYear(): number {
return this.birth_year;
}
getAbility(): string {
return this.ability;
}
}
const Bob = new Person('Bob', 'Jonason'); // error
const superMan = new SuperHero('Clark', 'Kent', 1938, 'fly');
superMan.introduce(); // Clark Kent can fly.
TypeScript有另一種類似於abstract class的寫法,是讓class用 implements
關鍵字去實作interface的方法,但因為還沒正式談到interface,所以這邊些略過不提。
不過明天就會提到interface了,好耶~
參考資料
TypeScript Official Docs
TypeScript Tutorial
W3Schools Online Web Tutorials
Classes in JS: Public, Private and Protected
Public class fields (JavaScript) @MDN
Private class features (JavaScript) @MDN
Difference between const and readonly in typescript